﻿using Mono.Cecil;
using System;
using System.Diagnostics;
using System.IO;
using System.Linq;
using System.Net;
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Security;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace CDude.Vita
{
    internal static class VitaCOMgui
    {
        private static readonly Version APP_VERSION = Assembly.GetExecutingAssembly().GetName().Version;
        private static readonly string executingpath = new Uri(Assembly.GetExecutingAssembly().GetName().CodeBase).LocalPath;
        private static readonly string PSM_PATH = Environment.GetEnvironmentVariable("SCE_PSM_SDK");
        private static readonly string PSS_PATH = Environment.GetEnvironmentVariable("SCE_PSM_SDK");
        private static readonly string PSM_DLL_PATH = PSM_PATH != null ? Path.Combine(PSM_PATH, "tools\\PsmStudio\\AddIns\\MonoDevelop.Psm\\MonoDevelop.Psm.dll") : null;
        private static readonly string PSS_DLL_PATH = PSS_PATH != null ? Path.Combine(PSM_PATH, "tools\\PssStudio\\AddIns\\MonoDevelop.Pss\\MonoDevelop.Pss.dll") : null;
        private static string vitacomguipath;

        private static bool CheckForUpdates()
        {
            WebClient webclient = new WebClient();
            try
            {
                string str_latestversion = webclient.DownloadString("http://data.celcodioc.host22.com/vitacom/latest_version");

                int[] latestversion = new int[2] { Convert.ToInt32(str_latestversion.Substring(0, str_latestversion.IndexOf('.'))), Convert.ToInt32(str_latestversion.Substring(2)) };
                if (latestversion[0] > APP_VERSION.Major || (latestversion[0] >= APP_VERSION.Major && latestversion[1] > APP_VERSION.Minor))
                {
                    DialogResult dialogresult = MessageBox.Show("Version " + str_latestversion + " is available. Do you want to update now?", "Update — VitaCOMgui", MessageBoxButtons.YesNo, MessageBoxIcon.Information);
                    if (dialogresult == DialogResult.Yes)
                    {
                        string updaterpath = webclient.DownloadString("http://data.celcodioc.host22.com/vitacom/updater_path");
                        webclient.DownloadFile(updaterpath, "VitaCOMupdater.exe");
                        Process.Start("VitaCOMupdater.exe", "\"" + System.AppDomain.CurrentDomain.FriendlyName + "\" new");
                        return true;
                    }
                }
            }
            catch { }
            finally
            {
                webclient.Dispose();
            }
            return false;
        }

        private static bool CopyApp()
        {
            try
            {
                bool isOn099 = true;

                bool copyexe = true;
                try
                {
                    if (File.ReadAllBytes(vitacomguipath).SequenceEqual(File.ReadAllBytes(executingpath))) copyexe = false;
                }
                catch
                {
                }
                try
                {
                    if (copyexe)
                        File.Copy(executingpath, vitacomguipath, true);
                }
                catch (IOException ex)
                {
                }

                new Process()
                {
                    StartInfo = new ProcessStartInfo(vitacomguipath)
                    {
                    }
                }.Start();

                return isOn099;
            }
            catch (Exception ex)
            {
                if (GlobalTools.SaveException(ex))
                    MessageBox.Show("Error. Please send error_log_gui.txt to celcodioc@gmail.com", "Error! – VitaCOMgui", MessageBoxButtons.OK, MessageBoxIcon.Error);
                Environment.Exit(0);

                return false;
            }
        }

        private static string getProgramFilesx86Path()
        {
            string win64path = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86);
            if (string.IsNullOrEmpty(win64path)) return Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
            else return win64path;
        }

        private static bool IsOn099()
        {
            bool result;
            if (Environment.GetEnvironmentVariable("SCE_PSM_SDK") == null)
            {
                if (Environment.GetEnvironmentVariable("SCE_PSS_SDK") != null)
                    result = false;
                else
                    result = true;
            }
            else
                result = true;

            return result;
        }

        private static Assembly LoadDLL(object sender, ResolveEventArgs e)
        {
            try
            {
                if (e.Name.Substring(0, e.Name.IndexOf(',')).EndsWith(".Ide") || e.Name.Substring(0, e.Name.IndexOf(',')).EndsWith(".Psm"))
                    return Assembly.LoadFrom(e.Name.Substring(0, e.Name.IndexOf(',')) + ".dll_VCg.dll");
                else if (e.Name.Substring(0, e.Name.IndexOf(',')).EndsWith(".Tasks"))
                    return Assembly.LoadFrom(Path.Combine(getProgramFilesx86Path(), "MSBuild\\Sce\\", e.Name.Substring(0, e.Name.IndexOf(',')) + ".dll"));
                else return null;
            }
            catch (Exception ex)
            {
                MessageBox.Show("Could not load required DLLs. Make sure the PlayStation Mobile SDK is installed on your computer.");
                GlobalTools.SaveException(ex);
                Environment.Exit(0);
                return null;
            }
        }

        private static bool NotOn100()
        {
            if (File.Exists(Path.Combine(PSM_PATH, "tools\\sdk_version.txt")) && File.ReadAllText(Path.Combine(PSM_PATH, "tools\\sdk_version.txt"))[0] != '0')
                return false;
            else
                return true;
        }

        [STAThread]
        private static void Main()
        {
            Application.EnableVisualStyles();
            Application.SetCompatibleTextRenderingDefault(false);

            if (PSS_PATH == null)
            {
                DialogResult result = PSM_PATH != null ?
                    MessageBox.Show("VitaCOM detected that the PlayStation Suite SDK 0.98 is installed on your computer. VitaCOM requires version 1.00 or greater to operate.\n\nDo you want to download it now?", "Error! – VitaCOMgui", MessageBoxButtons.YesNo, MessageBoxIcon.Error)
                    :
                    MessageBox.Show("Could not find PSM install path. Make sure the PlayStation Mobile SDK is installed on your computer.\n\nDo you want to download it now?", "Error! – VitaCOMgui", MessageBoxButtons.YesNo, MessageBoxIcon.Error);

                if (result == DialogResult.Yes)
                    Process.Start("http://psm-resource.np.dl.playstation.net/psm_resource/general/all/PSM_SDK_1.00.00.exe");
                Environment.Exit(0);
            }

            if (NotOn100())
            {
                DialogResult result = MessageBox.Show("VitaCOM detected that the PlayStation Mobile SDK 0.99 is installed on your computer. VitaCOM requires version 1.00 or greater to operate.\n\nDo you want to download it now?", "Error! – VitaCOMgui", MessageBoxButtons.YesNo, MessageBoxIcon.Error);

                if (result == DialogResult.Yes)
                    Process.Start("http://psm-resource.np.dl.playstation.net/psm_resource/general/all/PSM_SDK_1.00.00.exe");
                Environment.Exit(0);
            }

            vitacomguipath = Path.Combine(PSM_PATH, "tools\\PsmStudio\\AddIns\\MonoDevelop.Psm\\VitaCOMgui.exe");

            OperationInProgressForm tempprogressform = null;
            new Task(() =>
            {
                tempprogressform = new OperationInProgressForm(null) { Visible = true };
            });

            if (vitacomguipath.ToLower() != executingpath.ToLower())
            {
                if (!CheckForUpdates())
                {
                    if (IsOn099())
                        PatchDLL();

                    CopyApp();
                }
                return;
            }

            if (tempprogressform != null)
                tempprogressform.Dispose();

            AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(LoadDLL);

            Application.Run(new VCg_MainForm());
        }

        private static void PatchDLL()
        {
            AssemblyDefinition assembly = AssemblyDefinition.ReadAssembly(PSM_DLL_PATH);

            CustomAttribute internalattr = new CustomAttribute(assembly.MainModule.Import(typeof(InternalsVisibleToAttribute).GetConstructor(new Type[] { typeof(string) })));
            internalattr.ConstructorArguments.Add(new CustomAttributeArgument(assembly.MainModule.TypeSystem.String, "VitaCOMgui"));

            assembly.CustomAttributes.Add(internalattr);

            //assembly.CustomAttributes.Add(new CustomAttribute(assembly.MainModule.Import(typeof(AllowPartiallyTrustedCallersAttribute).GetConstructor(Type.EmptyTypes))));

            //PsmDevice
            foreach (MethodDefinition md in assembly.MainModule.Types[7].Methods)
                md.CustomAttributes.Add(new CustomAttribute(assembly.MainModule.Import(typeof(SecuritySafeCriticalAttribute).GetConstructor(Type.EmptyTypes))));

            //PsmDeviceConnection
            foreach (MethodDefinition md in assembly.MainModule.Types[8].Methods)
                md.CustomAttributes.Add(new CustomAttribute(assembly.MainModule.Import(typeof(SecuritySafeCriticalAttribute).GetConstructor(Type.EmptyTypes))));

            //VitaSerialPort
            foreach (MethodDefinition md in assembly.MainModule.Types[21].Methods)
                md.CustomAttributes.Add(new CustomAttribute(assembly.MainModule.Import(typeof(SecuritySafeCriticalAttribute).GetConstructor(Type.EmptyTypes))));

            assembly.Write(PSM_DLL_PATH + "_VCg.dll");
        }
    }
}